package com.util.docx;

import cn.hutool.core.collection.ListUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.util.excel.Listener;
import org.docx4j.XmlUtils;
import org.docx4j.model.listnumbering.Emulator;
import org.docx4j.model.listnumbering.ListNumberingDefinition;
import org.docx4j.model.table.TblFactory;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.openpackaging.parts.WordprocessingML.NumberingDefinitionsPart;
import org.docx4j.wml.*;

import java.io.File;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @Description: TODO
 * @Author: hechaobo
 * @Date: 2023/8/17
 **/
public class DocxUtil {
    // Create the package
    WordprocessingMLPackage wordMLPackage;
    int writableWidthTwips;//表格宽度

    public static enum highLightColors {
        yellow, green, red
    }

    List contents = new LinkedList();

    public DocxUtil() {
        try {
            wordMLPackage = WordprocessingMLPackage.createPackage();
            writableWidthTwips = wordMLPackage.getDocumentModel()
                    .getSections().get(0).getPageDimensions().getWritableWidthTwips();
        } catch (InvalidFormatException e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取docx内容
     *
     * @param path
     * @return
     * @throws Docx4JException
     */
    public static List<Object> readDocx(String path) throws Docx4JException {
        // Load the source document containing the table
        WordprocessingMLPackage srcDoc = WordprocessingMLPackage.load(new File(path));

        // Get the main document part of the source document
        MainDocumentPart srcMainPart = srcDoc.getMainDocumentPart();

        // Find the first table in the source document
        List<Object> srcContent = srcMainPart.getContent();
        return srcContent;
    }

    public void createWord(String path) throws Exception {

        NumberingDefinitionsPart ndp = new NumberingDefinitionsPart();
        wordMLPackage.getMainDocumentPart().addTargetPart(ndp);
        ndp.unmarshalDefaultNumbering();
        // Create a new numbering definition
        Numbering num = new Numbering();
        ndp.setJaxbElement(num);

        Numbering numbering = ndp.getJaxbElement();

        Numbering.AbstractNum abstractNum = new Numbering.AbstractNum();
        abstractNum.setAbstractNumId(BigInteger.valueOf(0));
        Numbering.AbstractNum.MultiLevelType value = new Numbering.AbstractNum.MultiLevelType();
        value.setVal("multilevel");
        abstractNum.setMultiLevelType(value);

        abstractNum.getLvl().add(createLevel("%1.", 0)); // 顶级级别
        abstractNum.getLvl().add(createLevel("%1.%2.", 1)); // 第二级级别
        abstractNum.getLvl().add(createLevel("%1.%2.%3.", 2)); // 第三级级别
        abstractNum.getLvl().add(createLevel("%1.%2.%3.%4.", 3)); // 第三级级别

        numbering.getAbstractNum().add(abstractNum);

        // Create the numbering references
        Numbering.Num num1 = createNum(1, 0);


        numbering.getNum().add(num1);

        //添加元素
        contents.forEach(o -> {
            wordMLPackage.getMainDocumentPart().getContent().add(o);
        });
        //设置h1大小
        Style headingStyle = wordMLPackage.getMainDocumentPart().getStyleDefinitionsPart().getStyleById("Heading1");
        Style headingStyle2 = wordMLPackage.getMainDocumentPart().getStyleDefinitionsPart().getStyleById("Heading2");
        // 设置样式
        RPr rpr = headingStyle.getRPr();
        if (rpr == null) {
            rpr = new RPr();
            headingStyle.setRPr(rpr);
        }
        Color color = new Color();
        color.setVal("#000000");
        rpr.setColor(color);
        headingStyle2.getRPr().setColor(color);
        HpsMeasure fontSize = new HpsMeasure();
        fontSize.setVal(BigInteger.valueOf(50)); // 设置字体大小为50
        rpr.setSz(fontSize);
        try {
            wordMLPackage.save(new File(path));
        } catch (Docx4JException e) {
            e.printStackTrace();
        }
    }


    private static Lvl createLevel(String format, int level) {
        Lvl lvl = new Lvl();
        lvl.setIlvl(BigInteger.valueOf(level));
        lvl.setNumFmt(new NumFmt() {{
            setVal(NumberFormat.DECIMAL);
        }});
        lvl.setStart(new Lvl.Start() {{
            setVal(BigInteger.ONE);
        }});
        lvl.setLvlText(new Lvl.LvlText() {{
            setVal(format);
        }});
        return lvl;
    }

    private static Numbering.Num createNum(int id, int abstractNumId) {
        Numbering.Num num = new Numbering.Num();
        num.setNumId(BigInteger.valueOf(id));
        Numbering.Num.AbstractNumId numId = new Numbering.Num.AbstractNumId();
        numId.setVal(BigInteger.valueOf(abstractNumId));
        num.setAbstractNumId(numId);
        return num;
    }

    /**
     * 表题
     *
     * @param text
     * @param lv   级别
     * @return
     */
    public P addH(String text, int lv) {
        P p = new P();
        Text t = new Text();
        t.setValue(text);
        R r = new R();
        r.getContent().add(t);
        p.getContent().add(r);
        //设置标题
        PPr pPr = new PPr();
        PPrBase.PStyle style = new PPrBase.PStyle();
        style.setVal("Heading" + lv);
        pPr.setPStyle(style);
        p.setPPr(pPr);
        contents.add(p);
        return p;
    }


    public static R addRun(P p, String text) {
        R r = new R();
        Text t = new Text();
        t.setValue(text);
        r.getContent().add(t);
        p.getContent().add(r);
        return r;
    }

    /**
     * 段落
     *
     * @param text
     * @return
     */
    public P addP(String text) {
        P p = new P();
        Text t = new Text();
        t.setValue(text);
        R r = new R();
        r.getContent().add(t);
        p.getContent().add(r);
        contents.add(p);
        return p;
    }

    public Tbl addTable(List<List<String>> data) {
        if (data == null || data.size() == 0) return null;
        int col = data.get(0).size();
        int width = writableWidthTwips / col;
        Tbl tbl = TblFactory.createTable(data.size(), col, width);
        List<Object> rows = tbl.getContent();

        for (int i = 0; i < rows.size(); i++) {
            Tr tr = (Tr) rows.get(i);
            List<Object> cells = tr.getContent();
            for (int j = 0; j < col; j++) {
                Tc td = (Tc) cells.get(j);
                P p = new P();
                Text t = new Text();
                try {
                    t.setValue(data.get(i).get(j));
                } catch (Exception e) {
                    continue;
                }
                R r = new R();
                if (i == 0) {
                    Color color = new Color();
                    color.setVal("#00aa00");
                    RPr rPr = new RPr();
                    rPr.setColor(color);
                    BooleanDefaultTrue b = new BooleanDefaultTrue();
                    rPr.setB(b);
                    r.setRPr(rPr);
                }
                r.getContent().add(t);
                p.getContent().add(r);
                td.getContent().add(p);
            }
        }
        contents.add(tbl);
        return tbl;
    }

    /**
     * 设置颜色
     *
     * @param run
     * @param fontColor #00ff00
     */
    public static void setColor(R run, String fontColor) {
        Color color = new Color();
        color.setVal(fontColor);
        RPr rPr = getRPr(run);
        rPr.setColor(color);
        run.setRPr(rPr);
    }

    /**
     * 加粗
     *
     * @param run
     */
    public static void setBold(R run) {
        RPr rPr = getRPr(run);
        rPr.setB(new BooleanDefaultTrue());
        run.setRPr(rPr);
    }

    /**
     * 添加编号
     */
    public static void addNumber(P p, int idx, int lv) {

        PPr pPr = p.getPPr();
        PPrBase.NumPr numPr = new PPrBase.NumPr();
        PPrBase.NumPr.NumId numId = new PPrBase.NumPr.NumId();
        numId.setVal(BigInteger.valueOf(idx));
        numPr.setNumId(numId);
        PPrBase.NumPr.Ilvl ilvl = new PPrBase.NumPr.Ilvl();
        ilvl.setVal(BigInteger.valueOf(lv));
        numPr.setIlvl(ilvl);
        pPr.setNumPr(numPr);
    }

    public static void setSize(R run, String fontSize) {
        RPr rPr = getRPr(run);
        HpsMeasure size = new HpsMeasure();
        size.setVal(new BigInteger(fontSize));
        rPr.setSz(size);
        rPr.setSzCs(size);
        run.setRPr(rPr);
    }

    public static void setHighLight(R run, String color) {
        Highlight highlight = new Highlight();
        highlight.setVal(color); // 设置高亮颜色，可以是"yellow"、"green"、"cyan"等
        RPr rPr = getRPr(run);
        rPr.setHighlight(highlight);
        run.setRPr(rPr);
    }

    private static RPr getRPr(R run) {
        return run.getRPr() == null ? new RPr() : run.getRPr();
    }

    public static void main(String[] args) {
        createFromXlsx("D:\\temp\\t.xlsx");
    }

    public static void simple() {


        DocxUtil docxUtil = new DocxUtil();
        docxUtil.addH("光猫下挂终端数量小于20个", 1);
        docxUtil.addP("这是一段文字");
        R run = (R) docxUtil.addP("第二段文字").getContent().get(0);
        setHighLight(run, highLightColors.green.name());
        setSize(run, "50");
        run = (R) docxUtil.addH("标题", 1).getContent().get(0);
        setColor(run, "#00ff00");
        setHighLight(run, highLightColors.yellow.name());
        docxUtil.addH("标题2", 2);
        docxUtil.addH("标题3", 3);
        List<List<String>> list = new ArrayList();
        for (int i = 0; i < 8; i++) {
            List<String> list2 = new ArrayList<String>();
            for (int j = 0; j < 4; j++) {
                list2.add("阿汉擦边");
            }
            list.add(list2);
        }
        docxUtil.addTable(list);
        try {
            docxUtil.createWord("/temp/2404/test.docx");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据excel的数据，生成word，有几列就生成几级标题
     *
     * @param path
     */
    public static void createFromXlsx(String path) {
        DocxUtil docxUtil = new DocxUtil();
        List<List<String>> data = new LinkedList<>();
        EasyExcel.read(path, new Listener() {
            @Override
            public void invoke(Map<Integer, String> integerStringMap, AnalysisContext analysisContext) {
                data.add(ListUtil.toList(integerStringMap.values()));
            }
        }).sheet().headRowNumber(0).doRead();

        dfs(data, 0, docxUtil);

        try {
            docxUtil.createWord("/temp/t.docx");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @param data xlsx里的数据
     * @param idx 当前第几列，级别
     * @param docxUtil
     */
    private static void dfs(List<List<String>> data, int idx, DocxUtil docxUtil) {
        //当所有列遍历完才退出
        if (data.get(0).size() == idx) return;
        //和excel顺序保持一致
        LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
        data.forEach(d -> {
            linkedHashSet.add(d.get(idx));
        });
        Map<String, List<List<String>>> collect = data.stream().collect(Collectors.groupingBy(l -> l.get(idx)));
        int i=1;
        //遍历当前级别
        for (String s : linkedHashSet) {
            //最后一级不加编号
            if(idx==data.get(0).size()-1){
                docxUtil.addP("("+i+++")"+s);
            }
            else {
                P p = docxUtil.addH(s, idx + 1);
                addNumber(p, 1, idx);
            }
            //遍历当前级别的下一级
            dfs(collect.get(s), idx + 1, docxUtil);
        }

    }

}
